import 'package:collection/collection.dart';
import "package:meta/meta.dart";

import 'sub_models/choice.dart';
import 'sub_models/usage.dart';

export 'sub_models/choice.dart';
export 'sub_models/usage.dart';
export 'stream/completion.dart';

/// {@template openai_completion_model}
/// This represents the response from a completion request by the [OpenAICompletion] methods.
/// {@endtemplate}
@immutable
final class OpenAICompletionModel {
  /// The [id]entifier of the completion.
  final String id;

  /// The date the completion was [created].
  final DateTime created;

  /// The [model] used to generate the completion.
  final String model;

  /// The [choices] generated by the completion.
  final List<OpenAICompletionModelChoice> choices;

  /// The [usage] of the completion, if any.
  final OpenAICompletionModelUsage? usage;

  /// The [systemFingerprint] of the completion, if any.
  final String? systemFingerprint;

  /// Weither the completion have at least one choice in [choices].
  bool get haveChoices => choices.isNotEmpty;

  /// Weither the completion have system fingerprint.
  bool get haveSystemFingerprint => systemFingerprint != null;

  /// Weither the completion have usage information.
  bool get haveUsage => usage != null;

  @override
  int get hashCode {
    return id.hashCode ^
        created.hashCode ^
        model.hashCode ^
        choices.hashCode ^
        usage.hashCode ^
        systemFingerprint.hashCode;
  }

  /// {@macro openai_completion_model}
  const OpenAICompletionModel({
    required this.id,
    required this.created,
    required this.model,
    required this.choices,
    required this.usage,
    required this.systemFingerprint,
  });

  /// {@macro openai_completion_model}
  /// This method is used to convert a [Map<String, dynamic>] object to a [OpenAICompletionModel] object.
  factory OpenAICompletionModel.fromMap(Map<String, dynamic> json) {
    return OpenAICompletionModel(
      id: json['id'],
      created: DateTime.fromMillisecondsSinceEpoch(json['created'] * 1000),
      model: json['model'],
      choices: (json['choices'] as List)
          .map((i) => OpenAICompletionModelChoice.fromMap(i))
          .toList(),
      usage: json['usage'] != null
          ? OpenAICompletionModelUsage.fromMap(json['usage'])
          : null,
      systemFingerprint: json['system_fingerprint'],
    );
  }

  @override
  String toString() {
    return 'OpenAICompletionModel(id: $id, created: $created, model: $model, choices: $choices, usage: $usage, systemFingerprint: $systemFingerprint)';
  }

  @override
  bool operator ==(covariant OpenAICompletionModel other) {
    if (identical(this, other)) return true;
    final listEquals = const DeepCollectionEquality().equals;

    return other.id == id &&
        other.created == created &&
        other.model == model &&
        listEquals(other.choices, choices) &&
        other.usage == usage &&
        other.systemFingerprint == systemFingerprint;
  }
}
